﻿using System;
using System.Messaging;
using System.Security.Principal;
using Autofac;
using ShuttleBus.Serialization;
using ShuttleBus.Subscriptions;
using ShuttleBus.Threading;

namespace ShuttleBus.Transports.Msmq
{
    [TransportModule("msmq")]
    public class MsmqModule : Module
    {
        private readonly int _threadCount;
        private readonly Uri _transportEndpoint;
        private readonly int _secondsToProcessMessage;

        public MsmqModule(int threadCount, Uri transportUri, int secondsToProcessMessage)
        {
            _threadCount = threadCount;
            _transportEndpoint = transportUri;
            _secondsToProcessMessage = secondsToProcessMessage;
        }

        protected override void Load(ContainerBuilder builder)
        {
            builder.Register(c => new JsonSerializer())
                     .As<ISerializer>()
                     .InstancePerLifetimeScope();

            builder.Register(c =>
                {
                    var path = _transportEndpoint.ToMsmqPath();

                    MessageQueue queue = MessageQueue.Exists(path) ? new MessageQueue(path) : MessageQueue.Create(path);

                    if (DateTime.Now.Subtract(TimeSpan.FromSeconds(10)) < queue.CreateTime)
                    {
                        SetPermissions(queue);
                    }
                    
                    queue.Formatter = new BinaryMessageFormatter();

                    return queue;
                })
                   .As<MessageQueue>()
                   .SingleInstance();

            builder.Register(c => new MsmqQueue(c.Resolve<IEndpoint>(),
                                                _secondsToProcessMessage,
                                                c.Resolve<MessageQueue>()))
                   .As<IQueue>()
                   .InstancePerDependency();

            builder.Register(c =>
                {
                    var repository = new MsmqSubscriptionRepository(c.Resolve<MessageQueue>(),
                                                                    c.Resolve<ICreateMessages>(),
                                                                    c.Resolve<ISerializer>(),
                                                                    c.Resolve<ICreateStreams>(),
                                                                    c.Resolve<IReadStreams>());
                    repository.Initialize();
                    return repository;
                })
                   .As<ISubscriptionRepository>()
                   .InstancePerLifetimeScope();

            builder.Register(c => new ThreadFactory())
                .As<IThreadFactory>()
                .InstancePerLifetimeScope();

            builder.Register(
                c =>
                    {
                        var context = c.Resolve<IComponentContext>();
                        return new MsmqEndpoint(_transportEndpoint, context.Resolve<IQueue>);
                    })
                   .As<IEndpoint>()
                   .InstancePerLifetimeScope();

            builder.Register(c => new MsmqTransport(c.Resolve<IEndpoint>(),
                                                   c.Resolve<ISerializer>(),
                                                   _threadCount,
                                                   c.Resolve<ICreateStreams>(),
                                                   c.Resolve<IReadStreams>(),
                                                   0))
                .As<ITransport>()
                .InstancePerLifetimeScope();
        }

        private static void SetPermissions(MessageQueue queue)
        {
            var user1 = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null).Translate(typeof(NTAccount)).ToString();
            var user2 = new SecurityIdentifier(WellKnownSidType.WorldSid, null).Translate(typeof(NTAccount)).ToString();
            var user3 = new SecurityIdentifier(WellKnownSidType.AnonymousSid, null).Translate(typeof(NTAccount)).ToString();
            queue.SetPermissions(user1, MessageQueueAccessRights.FullControl, AccessControlEntryType.Allow);
            queue.SetPermissions(user2, MessageQueueAccessRights.WriteMessage, AccessControlEntryType.Allow);
            queue.SetPermissions(user3, MessageQueueAccessRights.WriteMessage, AccessControlEntryType.Allow);
        }
    }
}